package com.weilele.mvvm.utils.fragment

import android.os.Bundle
import android.view.View
import androidx.annotation.IdRes
import androidx.annotation.NonNull
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.*
import androidx.lifecycle.lifecycleScope
import com.weilele.mvvm.MvvmConf
import com.weilele.mvvm.R
import com.weilele.mvvm.adapter.ignoreError
import com.weilele.mvvm.base.MvvmActivity
import com.weilele.mvvm.base.MvvmDialog
import com.weilele.mvvm.utils.`object`.ScreenAdaptationObj
import kotlinx.coroutines.CoroutineScope


/**
 * 添加fragment
 */
fun AppCompatActivity?.addFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment,
    tag: String? = null,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.supportFragmentManager?.addFragment(containerViewId, fragment, tag, beforeCommit)
}

/**
 * 添加fragment
 */
fun Fragment?.addFragment(
    @IdRes containerViewId: Int,
    @NonNull fragment: Fragment,
    tag: String? = null,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.childFragmentManager?.addFragment(containerViewId, fragment, tag, beforeCommit)
}

/**
 * 添加fragment
 */
fun FragmentManager?.addFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment, tag: String? = null,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.apply {
        beginTransaction()
            .also {
                beforeCommit?.invoke(it)
            }
            .add(containerViewId, fragment, tag)
            .commitAllowingStateLoss()
    }
}

/**
 * 添加fragment
 */
fun AppCompatActivity?.addFragment(
    tag: String,
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.supportFragmentManager?.addFragment(tag, fragment, beforeCommit)
}

/**
 * 添加fragment
 */
fun Fragment?.addFragment(
    tag: String,
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.childFragmentManager?.addFragment(tag, fragment, beforeCommit)
}

/**
 * 添加fragment
 * 不会被当作view 显示在界面上
 */
fun FragmentManager?.addFragment(
    tag: String,
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.apply {
        beginTransaction()
            .also {
                beforeCommit?.invoke(it)
            }
            .add(fragment, tag)
            .commitAllowingStateLoss()
    }
}

/**
 * 移除fragment
 */
fun Fragment?.removeFragment(
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.childFragmentManager?.removeFragment(fragment, beforeCommit)
}

fun AppCompatActivity?.removeFragment(
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.supportFragmentManager?.removeFragment(fragment, beforeCommit)
}

/**
 * 移除fragment
 */
fun FragmentManager?.removeFragment(
    @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.apply {
        beginTransaction()
            .also {
                beforeCommit?.invoke(it)
            }
            .remove(fragment)
            .commitAllowingStateLoss()
    }
}

/**
 * replace fragment
 */
fun Fragment?.replaceFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.childFragmentManager?.replaceFragment(containerViewId, fragment, beforeCommit)
}

/**
 * replace fragment
 */
fun AppCompatActivity?.replaceFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.supportFragmentManager?.replaceFragment(containerViewId, fragment, beforeCommit)
}

/**
 * replace fragment
 */
fun FragmentManager?.replaceFragment(
    @IdRes containerViewId: Int, @NonNull fragment: Fragment,
    beforeCommit: (FragmentTransaction.() -> Unit)? = null
) {
    this?.apply {
        beginTransaction()
            .also {
                beforeCommit?.invoke(it)
            }
            .replace(containerViewId, fragment)
            .commitAllowingStateLoss()
    }
}

/**
 * 设置携带数据
 */
fun <T : Fragment> T.arguments(bundle: Bundle? = null, setBundleValue: (Bundle.() -> Unit)?): T {
    val arg = bundle ?: Bundle()
    setBundleValue?.invoke(arg)
    arguments = arg
    return this
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> Fragment?.navigateTo(
    containerViewId: Int, fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    return this?.childFragmentManager.navigateTo(
        containerViewId,
        fg,
        hideTopStackFragment,
        beforeCommit
    )
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> AppCompatActivity?.navigateTo(
    containerView: View, fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    if (containerView.id == View.NO_ID) {
        containerView.id = View.generateViewId()
    }
    return this?.supportFragmentManager.navigateTo(
        containerView.id,
        fg,
        hideTopStackFragment,
        beforeCommit
    )
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> Fragment?.navigateTo(
    containerView: View, fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    if (containerView.id == View.NO_ID) {
        containerView.id = View.generateViewId()
    }
    return this?.childFragmentManager.navigateTo(
        containerView.id,
        fg,
        hideTopStackFragment,
        beforeCommit
    )
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> AppCompatActivity?.navigateTo(
    containerViewId: Int, fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    return this?.supportFragmentManager.navigateTo(
        containerViewId,
        fg,
        hideTopStackFragment,
        beforeCommit
    )
}

/**
 * 添加fragment，并且默认添加到回退栈
 * 注意生命周期回调问题
 */
fun <T : Fragment> FragmentManager?.navigateTo(
    containerViewId: Int,
    fg: T?,
    hideTopStackFragment: Boolean = true,
    beforeCommit: ((transition: FragmentTransaction, topStackFragment: Fragment?) -> Unit)? = null
): T? {
    this ?: return null
    fg ?: return null
    val lastFg = ignoreError {
        if (backStackEntryCount > 0) {
            findFragmentByTag(getBackStackEntryAt(backStackEntryCount - 1).name)
        } else {
            null
        }
    }
    val tag = fg.hashCode().toString()
    addFragment(containerViewId, fg, tag) {
        setCustomAnimations(
            R.anim.activity_open_enter, R.anim.activity_open_exit,
            R.anim.activity_close_enter, R.anim.activity_close_exit
        )
        addToBackStack(tag)
        if (hideTopStackFragment) {
            lastFg?.let {
                hide(it)
            }
        }
        beforeCommit?.invoke(this, lastFg)
    }
    return fg
}

/**
 *  当fragment的生命周期是onCreate时回到
 *  仅回调一次
 */
fun <T : Fragment> T.whenFirstCreated(block: CoroutineScope.(fragment: T) -> Unit) {
    this.lifecycleScope.launchWhenCreated {
        block.invoke(this, this@whenFirstCreated)
    }
}

/**
 *  当fragment的生命周期是onCreate时回到
 *  仅回调一次
 */
fun <T : Fragment> T.whenFirstStarted(block: CoroutineScope.(fragment: T) -> Unit) {
    this.lifecycleScope.launchWhenStarted {
        block.invoke(this, this@whenFirstStarted)
    }
}

/**
 *  当fragment的生命周期是onCreate时回到
 *  仅回调一次
 */
fun <T : Fragment> T.whenFirstResumed(block: CoroutineScope.(fragment: T) -> Unit) {
    this.lifecycleScope.launchWhenResumed {
        block.invoke(this, this@whenFirstResumed)
    }
}

/**
 * 添加对话框消失监听
 */
fun <T : MvvmDialog> T.addOnDismissListenerExt(listener: Function1<T, Unit>): Function1<MvvmDialog, Unit> {
    val dialogListener: Function1<MvvmDialog, Unit> = object : Function1<MvvmDialog, Unit> {
        override fun invoke(p1: MvvmDialog) {
            listener.invoke(this@addOnDismissListenerExt)
        }

    }
    addOnDismissListener(dialogListener)
    return dialogListener
}